import { clamp, JsonAsset, sys } from "cc";
import { BasicSystem } from "../Basic/BasicSystem";
import EventManager from "../Managers/EventManager";
import { EventTypes } from "../Managers/EventTypes";
import { clog } from "../Tools/ColorLog";
import Loader from "../Tools/Loader";
import { LevelDataTmp, MissionData, StorageTemp } from "./StorageTemp";
import { GlobalTmpData } from "../Config/GlobalTmpData";
import { GlobalConfig } from "../Config/GlobalConfig";
import { GlobalEnum } from "../Config/GlobalEnum";
import Tools from "../Tools/Tools";

export class StorageSystem extends BasicSystem {
    //存档名称+版本号构成存储的Id
    private static _storageName = 'MoveCar' + '01';
    private static _data: StorageTemp;

    public static init(d?: any): void {
        if (this.isInit) return;
        this.isInit = true;

        let newData = new StorageTemp();
        //获取存档
        let data = sys.localStorage.getItem(this._storageName);
        if (data) {
            //解析数据
            let res = JSON.parse(data);
            if (res) {
                //同步新的数据结构到存档中
                this.copyObject(res, newData);
                this._data = res;
            } else {
                //解析失败 
                this._data = newData;
            }
        } else {
            this._data = newData;
        }
        //再次存档一次
        this.saveData();
        //读取关卡数据
        this.loadLevelData();

        // 监听事件
        this.onEvents();

        this.initMissionData();
    }

    //#region ----事件

    private static onEvents() {
        EventManager.on(EventTypes.SDKEvents.InitSdkFinished, this.onInitSdkFinished, this);
    }

    private static onInitSdkFinished() {
        this.setExpParams();
    }
    //#endregion

    //#region ----实验参数->调整数值
    /**
     * 设置实验参数->sdk初始化之后调用
     */
    private static setExpParams() {
        // 第一次进入游戏
    }

    //#endregion

    //#region ----------------存档操作-对外--------------
    /**获取存档 */
    public static getData() {
        return this._data;
    }
    //获取json数据
    public static getJsonData(key: string) {
        return this._allJsonData[key];
    }
    /**
     * 修改存档
     * @param cb 
     * @param isSave 是否储存到本地
     */
    public static setData(cb: (d: StorageTemp) => void, isSave = false) {
        cb(this._data);
        isSave && this.saveData();
    }
    /**
     * 发送数据变更事件, 手动调用, 用于更新资源在UI上的显示
     * @param isAnim 如果时金币等资源变更需要动画显示 则传入true
     */
    public static updateToAssets(isAnim = false, isMask = false) {
        EventManager.emit(EventTypes.GameEvents.UserAssetsChanged, isAnim, isMask);
    }

    /**更新到存档中 */
    public static saveData() {
        sys.localStorage.setItem(this._storageName, JSON.stringify(this._data));
    }
    /**胜利后 关卡+1 */
    public static addLv() {
        if (GlobalTmpData.Game.isGuideLv) {
            this._data.levelAssets.guideLv++;
        } else {
            if (GlobalTmpData.isHardMode) {
                // 挑战模式
                this._data.levelAssets.hardLv += 1;
                this._data.levelAssets.hardRandLv = -1;
            } else {
                // 普通模式
                this._data.levelAssets.maxLv += 1;
                this._data.levelAssets.curLv = this._data.levelAssets.maxLv;
                // 清除随机关卡ID
                this._data.levelAssets.randLv = -1;
                this._data.levelAssets.randLvData = null;
            }
        }

        this.saveData();
    }

    /**获取指定关卡数据-默认为当前关卡数据-普通模式 */
    public static getLvData(lv?: number): LevelDataTmp {
        if (undefined === lv) {
            lv = this._data.levelAssets.curLv;
        }
        // 根据关卡计算章节 获取对应的关卡数据
        let dataName = 'LevelData';

        let _levelData = this.getJsonData(dataName);
        if (!_levelData) {
            clog.error('未找到数据');
        }

        let data = _levelData[lv] as LevelDataTmp;
        // 随机起始关卡
        const randStartLv = 50;
        // 随机的小范围
        const subRandRange = 5;

        // 随机关卡数据
        if (!data || lv > randStartLv) {

            // 获取随机ID -1 表示未随机
            let randLv = this._data.levelAssets.randLv;
            data = this._data.levelAssets.randLvData;

            if (randLv < 0) {
                // 取出后面指定数量的关卡随机
                let maxLv = Object.keys(this._levelData).length;
                if (maxLv < randStartLv) {
                    randLv = Math.floor((Math.random() * 0.5 + 0.45) * maxLv);
                } else {
                    // 上次随机关卡
                    let _preRandLv = this._data.levelAssets.preRandLv;
                    if (_preRandLv < randStartLv || _preRandLv + subRandRange >= maxLv) {
                        _preRandLv = randStartLv;
                    }
                    // 小范围内随机
                    const addRand = Math.floor(Math.random() * subRandRange) + 1;
                    randLv = addRand + _preRandLv;

                    if (randLv > maxLv) {
                        _preRandLv = randStartLv;
                        randLv = randStartLv + Math.ceil(Math.random() * (maxLv - randStartLv));
                    }

                }

                // 更新随机关卡
                this._data.levelAssets.randLv = randLv;
                this._data.levelAssets.preRandLv = randLv;
                //
                data = this._getRandLvData(this._levelData[randLv] as LevelDataTmp, lv);
                // 存档
                this._data.levelAssets.randLvData = data;
                this.saveData();
            }
            clog.warn('随机关卡数据:' + randLv);

        } else {
            // 非随机关卡
            data = this._data.levelAssets.randLvData;
            if (!data || Object.keys(data).length == 0) {
                data = this._getRandLvData(this._levelData[lv] as LevelDataTmp, lv);
                // 存档
                this._data.levelAssets.randLvData = data;
                this.saveData();
            }
        }

        if (!data) {
            clog.warn('未找到关卡数据', lv);
        } else {
            clog.tip('当前关卡:' + lv);
        }
        return data;
    }


    /**获取指定关卡数据-默认为当前关卡数据 */
    public static getGuideLvData(): LevelDataTmp {

        const lvData = this.getJsonData('GuideData');
        if (this._data.levelAssets.guideLv >= Object.keys(lvData).length) {
            this._data.levelAssets.guideLv = 1;
            this.saveData();
        }
        let data = lvData[this._data.levelAssets.guideLv] as LevelDataTmp;

        if (!data) {
            clog.warn('未找到新手关卡数据', this._data.levelAssets.guideLv);
        } else {
            clog.tip('新手关卡:' + this._data.levelAssets.guideLv);
        }
        return data;
    }

    //获取最大关卡数
    public static getMaxLvCount() {
        return Object.keys(this._levelData).length;
    }

    //#endregion

    //#region ---随机替换关卡数据
    private static _getRandLvData(d: LevelDataTmp, lv: number): LevelDataTmp {
        // 深拷贝
        const _data = JSON.parse(JSON.stringify(d)) as LevelDataTmp;
        // 随机名称
        let _names = [ ];

        const arr = [];

        // 替换概率
        // const randRate = Math.random() * 0.5 + 0.3;
        // 随机替换
        // for (let i = 0; i < _data.animArr.length; i++) {
        //     const e = _data.animArr[i];
        //     // 概率替换
        //     e.name = _names[Math.floor(Math.random() * _names.length)];
        // }

        return _data;
    }

    //#endregion

    //#region ----------------关卡数据---------
    /**关卡数据Bound */
    private static levelDataBound = 'LevelData';
    /**关卡数据 */
    private static levelDataJson = 'LevelData';

    /**关卡数据-数据格式*/
    private static _levelData: { [lv: number]: {} } = {};
    /**所有的json数据 */
    private static _allJsonData: { [name: string]: any } = {};

    //加载关卡数据
    private static loadLevelData() {
        Loader.loadBundle(this.levelDataBound, () => {
            //加载json数据
            Loader.loadBundleDir(this.levelDataBound, '/', (res: JsonAsset[]) => {
                res.forEach(e => {
                    this._allJsonData[e.name] = e.json;
                })
                this._levelData = this._allJsonData[this.levelDataJson];

                this.isInitFinished = true;
            }, JsonAsset, false);
        })
    }
    //#endregion

    //#region ----------------任务数据
    private static initMissionData() {
        EventManager.on(EventTypes.SDKEvents.VideoComplete, this.onVideoComplete, this);
        EventManager.on(EventTypes.SDKEvents.ShareComplete, this.onShareComplete, this);
        EventManager.on(EventTypes.GameEvents.GameOver, this.onGameOver, this);

        this.onDailyLogin();
    }

    // 记录任务-missionType
    private static recorderMissionData(missionType: GlobalEnum.MissionType) {
        const cfg = GlobalConfig.MissionCfg;
        if (!cfg) {
            return;
        }
        let isSave = false;
        for (const key in cfg) {
            const _type = cfg[key].type;
            if (_type == missionType) {
                this.saveMissionData(key, 1, false);
                isSave = true;
            }
        }
        if (isSave) {
            this.saveData();
        }
        EventManager.emit(EventTypes.MissionUIEvents.RefreshInfo);
    }
    // 记录每日登录
    private static onDailyLogin() {
        // 每日登录
        const isSameDay = Tools.isSameDay(this._data.userAssets.missionPreRefreshTime);
        if (!isSameDay) {
            this.recorderMissionData(GlobalEnum.MissionType.Login);
        }
    }
    // 记录分享
    private static onShareComplete(d?) {
        this.recorderMissionData(GlobalEnum.MissionType.Share);
    }

    // 记录视频播放
    private static onVideoComplete(d?) {
        this.recorderMissionData(GlobalEnum.MissionType.Video);
    }
    // 记录游戏胜利
    private static onGameOver(isWin) {
        if (isWin && !GlobalTmpData.Game.isGuideLv) {
            this.recorderMissionData(GlobalEnum.MissionType.PassLevel);
        }
    }

    // 获取任务数据
    public static getMissionData(isRefresh = false): { [key: number]: MissionData } {
        const userAssets = StorageSystem.getData().userAssets;
        // 检测任务记录  -> 初始化
        if (!userAssets.missionRecs) {
            userAssets.missionRecs = {};
        }
        const cfg = GlobalConfig.MissionCfg;
        let isSave = false;
        for (const key in cfg) {
            if (!userAssets.missionRecs[key]) {
                userAssets.missionRecs[key] = new MissionData();
                userAssets.missionRecs[key].idx = key;
                isSave = true;
            }
        }

        // ---- 每日任务重置
        // 判断任务时间
        if (!isRefresh) {
            isRefresh = !Tools.isSameDay(userAssets.missionPreRefreshTime);
        }
        // 超过一天 则重置
        if (isRefresh) {
            userAssets.missionPreRefreshTime = Date.now();
            const cfg = GlobalConfig.MissionCfg;
            for (const key in cfg) {
                let rec: MissionData = userAssets.missionRecs[key];
                if (!rec) {
                    rec = new MissionData();
                    rec.idx = key;
                    userAssets.missionRecs[key] = rec;
                }
                rec.isFinish = false;
                rec.isAward = false;
                rec.count = 0;
            }
            isSave = true;
        }

        isSave && this.saveData();

        // ---- 每周任务重置
        // 判断任务时间
        // if (!isRefresh) {
        //     isRefresh = !Tools.isSameWeek(userAssets.missionWeekPreRefreshTime);
        // }
        // // 超过一周 则重置
        // if (isRefresh) {
        //     userAssets.missionWeekPreRefreshTime = Date.now();
        //     const cfg = GlobalConfig.MissionCfg;
        //     for (const key in cfg) {
        //         let rec: MissionData = userAssets.missionWeekRecs[key];
        //         if (!rec) {
        //             rec = new MissionData();
        //             rec.idx = key;
        //             userAssets.missionWeekRecs[key] = rec;
        //         }
        //         rec.isFinish = false;
        //         rec.isAward = false;
        //         rec.count = 0;
        //     }
        //     isSave = true;
        // }
        //    isSave && this.saveData();


        return userAssets.missionRecs;
    }

    // 统计任务数据
    public static saveMissionData(misstionID: string, val: number, isSave = true) {
        const data = this.getMissionData();
        const rec = data[misstionID];
        rec.count += val;
        rec.isFinish = rec.count >= GlobalConfig.MissionCfg[misstionID].val;
        isSave && this.saveData();
        console.log(`# saveMissionData: ${misstionID} ,${rec.count}, ${rec.isFinish}`);
    }


    //#endregion

    /**数据类型变化时,复制旧的数据 到新的存档-*/
    private static copyObject(oldData: any, newData: any) {
        for (let key in newData) {
            switch (typeof newData[key]) {
                case "number":
                case "boolean":
                case "string": {
                    oldData[key] = undefined !== oldData[key] ? oldData[key] : newData[key];
                    break;
                }
                case "object": {
                    if (Array.isArray(newData[key])) {
                        if (undefined == oldData[key]) {
                            oldData[key] = [].concat(newData[key]);
                        }
                    } else {
                        if (undefined == oldData[key]) oldData[key] = {};
                        this.copyObject(oldData[key], newData[key]);
                    }
                    break;
                }
                default: {
                    break;
                }
            }
        }
    }
}
